import time
from collections import OrderedDict
from types import FunctionType
import datetime
from datetime import timedelta

from django import forms
from django.conf import settings
from django.contrib import messages
from django.db.models import Q, Sum
from django.http import JsonResponse
from django.shortcuts import render, redirect, reverse
from django.utils.safestring import mark_safe
from django_redis import get_redis_connection

from web import models
from web.utils.blackip import BlackIp
from web.utils.bootstrap import BootstrapForm
from web.utils.group import get_incomma_value
from web.utils.link import filter_reverse
from web.utils.pager import Pagernation


def get_total_risk_count(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(obj=None, is_header=None, *args, **kwargs):
        if is_header:
            return text

        return "{:,}".format(obj.total_risk_count())

    return inner


def get_association_devie(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(obj=None, is_header=None, *args, **kwargs):
        if is_header:
            return text
        date_time = obj.create_datetime
        data_list = []
        sangfor_exists = models.SangforFwAttackLog.objects.filter(create_datetime__year=date_time.year,
                                                                  create_datetime__month=date_time.month,
                                                                  create_datetime__day=date_time.day,
                                                                  src_ip=obj.src_ip).exists()
        if sangfor_exists:
            data_list.append('Sangfor_Fw')
        skyeye_exists = models.SkyEyeLog.objects.filter(create_datetime__year=date_time.year,
                                                        create_datetime__month=date_time.month,
                                                        create_datetime__day=date_time.day,
                                                        attack_sip=obj.src_ip).exists()
        if skyeye_exists:
            data_list.append('SkyEye')
        if len(data_list) > 0:
            data_str = "<a class='btn btn-danger btn-xs'>{}</a>".format(','.join(data_list))
        else:
            data_str = ""

        return mark_safe(data_str)

    return inner


def get_history_src_ip(text, field):
    """
    获取choice字段的文本
    :param text: choice字段的名称
    :param field: choice字段
    :return:
    """

    def inner(obj=None, is_header=None, *args, **kwargs):
        if is_header:
            return text
        create_datetime = obj.create_datetime
        attack_count = models.BlockIp.objects.filter(src_ip=obj.src_ip).exclude(
            create_datetime__year=create_datetime.year,
            create_datetime__month=create_datetime.month,
            create_datetime__day=create_datetime.day).count()
        if attack_count > 0:
            tpl = '<span style="color:red;">{}次</span>'.format(attack_count)
        else:
            tpl = ''
        return mark_safe(tpl)

    return inner


def botgate_attackip_list(request, pk):
    list_display = [
        'src_ip',
        get_history_src_ip('历史封禁','src_ip'),
        get_association_devie('关联报警', 'src_ip'),
        'location',
        get_total_risk_count('告警事件数', 'total_risk_count'),
        get_incomma_value('攻击总数', 'attack_count'),
        get_incomma_value('简单脚本攻击', 'no_cookie_count'),
        'replay_attack_count',
        get_incomma_value('高级自动化工具攻击', 'bot_attack_count'),
        get_incomma_value('漏洞攻击', 'inject_attack_count'),
        get_incomma_value('Nday攻击数', 'nday_count'),
        get_incomma_value('Nday类型数', 'nday_name_count'),
        get_incomma_value('扫描次数', 'scan_count'),
        'update_datetime'
    ]
    search_list = [
        'src_ip__contains',
        'location__contains'
    ]
    keyword = request.GET.get("keyword", "").strip()
    con = Q()
    if keyword:
        con.connector = "OR"
        for item in search_list:
            con.children.append((item, keyword))
    obj = models.Botgate.objects.filter(id=pk).first()
    data_time = obj.create_datetime
    queryset = models.BotgateAttackIp.objects.filter(con). \
        filter(create_datetime__year=data_time.year,
               create_datetime__month=data_time.month,
               create_datetime__day=data_time.day).order_by('-inject_attack_count', '-nday_count', '-attack_count')

    model_class = models.BotgateAttackIp
    pager = Pagernation(request, queryset)
    # 1.处理表头
    header_list = []
    if list_display:
        for key_or_func in list_display:
            if isinstance(key_or_func, FunctionType):
                header_list.append(key_or_func(None, True))
            else:
                verbose_name = model_class._meta.get_field(key_or_func).verbose_name
                header_list.append(verbose_name)
    else:
        header_list.append(model_class._meta.model_name)
    # 2.处理表格内容
    data_list = queryset[pager.start:pager.end]
    body_list = []
    for row in data_list:
        tr_list = []
        if list_display:
            for key_or_func in list_display:
                if isinstance(key_or_func, FunctionType):
                    tr_list.append(key_or_func(row, False))
                else:
                    tr_list.append('' if getattr(row, key_or_func) == None else getattr(row, key_or_func))
        else:
            tr_list.append(row)
        body_list.append(tr_list)
    pager = Pagernation(request, queryset)
    context = {
        'body_list': body_list,
        'header_list': header_list,
        'pager': pager,
        'keyword': keyword,
        'id': pk
    }
    return render(request, 'botgate_attackip_list.html', context)


class BotgateAttackIpModelForm(BootstrapForm, forms.ModelForm):
    location = forms.CharField(
        label='来源区域',
        required=True
    )

    class Meta:
        model = models.BotgateAttackIp
        fields = ['src_ip', 'location']


def botgate_attackip_add(request, pk):
    botgate_obj = models.Botgate.objects.filter(id=pk).first()
    create_datetime = botgate_obj.create_datetime
    if request.method == 'GET':
        form = BotgateAttackIpModelForm()
        return render(request, 'form.html', {'form': form})
    form = BotgateAttackIpModelForm(data=request.POST)

    if form.is_valid():
        src_ip = form.cleaned_data['src_ip']
        location = form.cleaned_data['location']
        attackip_obj = models.BotgateAttackIp.objects.filter(create_datetime__year=create_datetime.year,
                                                             create_datetime__month=create_datetime.month,
                                                             create_datetime__day=create_datetime.day,
                                                             src_ip=src_ip).first()
        if attackip_obj:
            form.add_error('src_ip', '你输入的IP在恶意IP库中已经存在，请重新输入！')
        else:
            models.BotgateAttackIp.objects.create(
                src_ip=src_ip,
                create_datetime=create_datetime,
                location=location.strip()
            )
            return redirect(filter_reverse(request, reverse('botgate_attackip_list', kwargs={'pk': pk})))
    return render(request, 'form.html', {'form': form})


def block_ip(request, pk, src_ip):
    current_datetime = datetime.datetime.now()
    current_time = current_datetime.time()
    specific_time = datetime.time(hour=15, minute=00, second=0, microsecond=0)
    if current_time > specific_time:
        specific_datetime = current_datetime + timedelta(days=1)
    else:
        specific_datetime = current_datetime
    botgate_obj = models.Botgate.objects.filter(id=pk).first()
    date_time = botgate_obj.create_datetime
    BotgateAttackIp_obj = models.BotgateAttackIp.objects.filter(create_datetime__year=date_time.year,
                                                                create_datetime__month=date_time.month,
                                                                create_datetime__day=date_time.day,
                                                                src_ip=src_ip).first()
    if not BotgateAttackIp_obj:
        messages.add_message(request, settings.MESSAGE_DANGER_TAG, 'IP不存在')
        return redirect(filter_reverse(request, reverse('botgate_attackip_list', kwargs={'pk': pk})))

    # 将封禁IP写入redis
    block_ip = BotgateAttackIp_obj.src_ip
    location = BotgateAttackIp_obj.location
    conn = get_redis_connection('default')
    user = request.session.get(settings.MOBILEDJ_SESSION_KEY)['id']
    blackip_obj = BlackIp(block_ip, 'botgate', settings.MANUAL_BLOCK, settings.BLOCK, user, specific_datetime, location)
    conn.lpush(settings.QUEUE_TASK_NAME, str(blackip_obj))
    messages.add_message(request, messages.SUCCESS, '恶意IP：{}在瑞数动态防御系统封禁成功'.format(block_ip))
    time.sleep(1)
    return redirect(filter_reverse(request, reverse('botgate_attackip_list', kwargs={'pk': pk})))


def statistics_attackip(request):
    pk = request.GET.get('id')
    obj = models.Botgate.objects.filter(id=pk).first()
    date_time = obj.create_datetime
    queryset = models.BotgateAttackIp.objects.filter(create_datetime__year=date_time.year,
                                                     create_datetime__month=date_time.month,
                                                     create_datetime__day=date_time.day). \
                   values('src_ip').annotate(ct=Sum('attack_count')).order_by('-ct')[:20]
    all_attackip_dict = OrderedDict()
    for item in queryset:
        all_attackip_dict[item['src_ip']] = {
            'name': item['src_ip'],
            'attack_count': item['ct']
        }

    categories = [data['name'] for data in all_attackip_dict.values()]

    data_result_dict = OrderedDict()
    data = [row['attack_count'] for row in all_attackip_dict.values()]
    data_result_dict['attack_count'] = {'name': '攻击总数', 'data': data}

    context = {
        'status': True,
        'data': {
            'categories': categories,
            'series': list(data_result_dict.values())
        }
    }

    return JsonResponse(context)
